package com.jztd.platfrom.base.util;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jztd.platfrom.base.model.Tree;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * 对象工具类
 * @author lj
 * @date 2025/08/03
 */
@Slf4j
public final class ObjectUtils {
    /**
     * 空字符串
     */
    public static final String EMPTY_STRING = "";

    /**
     * 空字节数组
     */
    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

    /**
     * CPU数量
     */
    public static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    /**
     * 对象转换映射工厂
     */
    private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();

    /**
     * 唯一标识日期格式化处理器
     */
    private static final DateTimeFormatter IDENTITY_DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    /**
     * Protostuff对象类型/Schema映射表
     */
    private static final Map<Class<?>, Schema<?>> PROTOSTUFF_CLASS_SCHEMA_MAPPING = new ConcurrentHashMap<>();

    /**
     * 类对象/属性描述映射表
     */
    private static final Map<Class<?>, List<PropertyDescriptor>> CLASS_PROPERTY_DESCRIPTOR_MAPPING =
            new ConcurrentHashMap<>();

    private ObjectUtils() {
    }

    /**
     * 返回不为空的对象（如果第一个对象为空，则返回第二个对象）
     *
     * @param object 目标对象
     * @param value  替换对象
     * @param <T>    对象类型泛型
     * @return 返回对象
     */
    public static <T> T ifNull(T object, T value) {
        return object == null ? value : object;
    }

    /**
     * 返回不为空的对象（如果第一个对象为空，则返回第二个对象）
     *
     * @param object   目标对象
     * @param supplier 替换对象提供方法
     * @param <T>      对象类型泛型
     * @return 返回对象
     */
    public static <T> T ifNull(T object, Supplier<T> supplier) {
        return object == null ? supplier == null ? null : supplier.get() : object;
    }

    /**
     * 返回不为空的对象（如果第一个对象为空，则返回第二个对象）
     *
     * @param object   目标对象
     * @param function 目标对象方法
     * @param <T>      目标对象类型泛型
     * @param <R>      返回对象类型泛型
     * @return 返回对象
     */
    public static <T, R> R ifNull(T object, Function<T, R> function) {
        return object == null || function == null ? null : function.apply(object);
    }

    /**
     * 返回不为空的对象（如果第一个对象为空，则返回第二个对象）
     *
     * @param object   目标对象
     * @param function 目标对象方法
     * @param supplier 替换对象提供方法
     * @param <T>      目标对象类型泛型
     * @param <R>      返回对象类型泛型
     * @return 返回对象
     */
    public static <T, R> R ifNull(T object, Function<T, R> function, Supplier<R> supplier) {
        return object == null || function == null ? ifNull(supplier, Supplier::get)
                : ifNull(function.apply(object), supplier);
    }

    /**
     * 获取对象属性值
     *
     * @param object 对象实例
     * @param field  属性字段对象
     * @return 属性值
     */
    public static Object getObjectProperty(Object object, @NonNull Field field) {
        if (object == null) {
            return null;
        }
        field.setAccessible(true);
        try {
            return field.get(object);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置对象属性值
     *
     * @param object 对象实例
     * @param field  属性字段对象
     * @param value  属性值
     */
    public static void setObjectProperty(Object object, @NonNull Field field, Object value) {
        if (object != null) {
            field.setAccessible(true);
            try {
                field.set(object, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 迭代对象字段
     *
     * @param clazz    目标对象类型
     * @param consumer 字段访问回调方法
     */
    public static void iterateClassFields(@NonNull Class<?> clazz, @NonNull BiConsumer<Field, Integer> consumer) {
        iterateClassFields(clazz, consumer, (field, index) -> false);
    }

    /**
     * 迭代对象字段
     *
     * @param clazz       目标对象类型
     * @param consumer    字段访问回调方法
     * @param interrupter 访问中断回调方法
     */
    public static void iterateClassFields(@NonNull Class<?> clazz, @NonNull BiConsumer<Field, Integer> consumer,
                                          @NonNull BiPredicate<Field, Integer> interrupter) {
        int index = 0;
        do {
            for (Field field : clazz.getDeclaredFields()) {
                if (interrupter.test(field, index)) {
                    break;
                }
                if (!Modifier.isStatic(field.getModifiers()) && !field.getName().startsWith("this$")) {
                    consumer.accept(field, index++);
                }
            }
        } while ((clazz = clazz.getSuperclass()) != null);
    }

    /**
     * 获取对象属性复制处理器工厂
     *
     * @return 对象属性复制处理器工厂
     */
    public static MapperFactory getCopierBuilder() {
        return MAPPER_FACTORY;
    }

    /**
     * 生成唯一标识
     * <p>
     * 唯一标识格式：yyyyMMddHHmmss + 四位随机数，例如：202108251933453756
     *
     * @return 唯一标识
     */
    public static long generateIdentity() {
        String time = IDENTITY_DATETIME_FORMATTER.format(LocalDateTime.now());
        return Long.parseLong(time + ThreadLocalRandom.current().nextInt(1000, 10000));
    }

    /**
     * 对象属性复制
     *
     * @param source 源对象实例
     * @param target 目标对象实例
     * @param <T>    源对象类型
     * @param <R>    目标对象类型
     */
    public static <T, R> void copying(T source, R target) {
        getCopierBuilder().getMapperFacade().map(source, target);
    }

    /**
     * 对象属性复制
     *
     * @param source 源对象实例
     * @param target 目标对象
     * @param <T>    源对象类型
     * @param <R>    目标对象类型
     * @return 目标对象实例
     */
    public static <T, R> R copying(T source, Class<R> target) {
        return getCopierBuilder().getMapperFacade().map(source, target);
    }

    /**
     * 对象属性复制
     *
     * @param sources 源对象实例列表
     * @param target  目标对象
     * @param <T>     源对象类型
     * @param <R>     目标对象类型
     * @return 目标对象实例列表
     */
    public static <T, R> List<R> copying(Collection<T> sources, Class<R> target) {
        if (CollectionUtils.isEmpty(sources)) {
            return Collections.emptyList();
        }
        MapperFacade facade = getCopierBuilder().getMapperFacade();
        return sources.stream().map(source -> facade.map(source, target)).collect(Collectors.toList());
    }

    /**
     * 获取Protostuff schema
     *
     * @param clazz 目标对象
     * @param <T>   目标对象类型
     * @return Schema对象实例
     */
    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getProtostuffSchema(Class<T> clazz) {
        return (Schema<T>) PROTOSTUFF_CLASS_SCHEMA_MAPPING.computeIfAbsent(clazz, k -> RuntimeSchema.getSchema(clazz));
    }

    /**
     * 序列化方法，把指定对象序列化成字节数组
     *
     * @param object 被序列化对象实例
     * @return 字节数组
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T object) {
        if (object == null) {
            return null;
        }
        Schema<T> schema = getProtostuffSchema((Class<T>) object.getClass());
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            return ProtobufIOUtil.toByteArray(object, schema, buffer);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 反序列化方法，将字节数组反序列化成指定Class类型
     *
     * @param bytes 字节数组
     * @param clazz 目标对象类型
     * @return 目标对象实例
     */
    public static <T> T deserialize(byte[] bytes, @NonNull Class<T> clazz) {
        if (bytes == null) {
            return null;
        }
        Schema<T> schema = getProtostuffSchema(clazz);
        T object = schema.newMessage();
        ProtobufIOUtil.mergeFrom(bytes, object, schema);
        return object;
    }

    /**
     * 将对象转换成键/值对
     *
     * @param object   对象实例
     * @param excludes 需要排除的属性数组
     * @return 键/值对
     */
    public static Map<String, Object> object2map(Object object, String... excludes) {
        if (object == null) {
            return Collections.emptyMap();
        }
        PropertyDescriptor[] properties = BeanUtils.getPropertyDescriptors(object.getClass());
        if (properties.length == 0) {
            return Collections.emptyMap();
        }
        Set<String> excludeProperties = excludes.length == 0 ? Collections.emptySet() : Sets.newHashSet(excludes);
        Map<String, Object> propertyValues = Maps.newHashMapWithExpectedSize(properties.length - excludes.length);
        iterateClassFields(object.getClass(), (field, index) -> {
            if (!excludeProperties.contains(field.getName())) {
                field.setAccessible(true);
                try {
                    propertyValues.put(field.getName(), field.get(object));
                } catch (ReflectiveOperationException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        return propertyValues;
    }

    /**
     * 树节点组装，自动关联树节点并返回根节点列表
     *
     * @param trees 树对象实例列表
     * @param <T>   树对象类型
     * @return 树根节点列表
     */
    public static <T extends Tree<?, T>> List<T> assembleTrees(Collection<T> trees) {
        if (CollectionUtils.isEmpty(trees)) {
            return Collections.emptyList();
        }

        // 构建树主键/实例映射表，并初始化树的子节点集合
        Map<?, T> mapping = trees.stream().peek(tree -> tree.setChildren(new LinkedList<>()))
                .collect(Collectors.toMap(Tree::getId, t -> t, (o, n) -> n));

        // 查找并关联树节点，返回所有没有父节点的树
        return trees.stream().filter(tree -> {
            T parent = ifNull(tree.getParentId(), mapping::get);
            if (parent != null) {
                parent.getChildren().add(tree);
            }
            return Objects.isNull(parent);
        }).collect(Collectors.toList());
    }

    /**
     * 遍历树节点
     *
     * @param trees    树对象列表
     * @param consumer 树节点处理回调方法
     * @param <T>      树对象类型
     */
    public static <T extends Tree<?, T>> void iterateTrees(Collection<T> trees, @NonNull Consumer<T> consumer) {
        if (!CollectionUtils.isEmpty(trees)) {
            trees.forEach(tree -> {
                consumer.accept(tree);
                iterateTrees(ifNull(tree, Tree::getChildren), consumer);
            });
        }
    }

    /**
     * 获取对象属性描述列表
     *
     * @param clazz 目标对象
     * @return 属性描述列表
     */
    public static List<PropertyDescriptor> getPropertyDescriptors(@NonNull Class<?> clazz) {
        return CLASS_PROPERTY_DESCRIPTOR_MAPPING.computeIfAbsent(clazz, c -> {
            BeanInfo bean;
            try {
                bean = Introspector.getBeanInfo(c, Object.class);
            } catch (IntrospectionException e) {
                throw new RuntimeException(e);
            }
            return Collections.unmodifiableList(Arrays.asList(bean.getPropertyDescriptors()));
        });
    }

    /**
     * 查找属性对应的Getter方法
     *
     * @param clazz    目标对象
     * @param property 属性名称
     * @return 方法对象
     */
    public static Method lookupPropertyGetter(@NonNull Class<?> clazz, @NonNull String property) {
        return getPropertyDescriptors(clazz).stream().filter(descriptor -> descriptor.getName().equals(property))
                .findAny().map(PropertyDescriptor::getReadMethod).orElse(null);
    }

    /**
     * 截取字符串
     *
     * @param original 原始字符串
     * @param index    开始下标（负数标识从字符串末尾开始）
     * @return 子字符串
     */
    public static String substring(String original, int index) {
        return original == null ? null :
                index < 0 ? original.substring(original.length() + index) : original.substring(index);
    }

    /**
     * 截取字符串
     *
     * @param original 原始字符串
     * @param index    开始下标（负数标识从字符串末尾开始）
     * @param length   截取长度
     * @return 子字符串
     */
    public static String substring(String original, int index, int length) {
        Assert.isTrue(length > -1, "length must be greater than -1");
        if (original == null) {
            return null;
        } else if (index < 0) {
            index += original.length();
        }
        return original.substring(index, Math.min(original.length(), index + length));
    }

    /**
     * 字符串左对齐
     *
     * @param original 原始字符串
     * @param length   字符串总长度
     * @return 对齐后的字符串
     */
    public static String ljust(String original, int length) {
        return ljust(original, length, ' ');
    }

    /**
     * 字符串左对齐
     *
     * @param original 原始字符串
     * @param length   字符串总长度
     * @param c        补齐字符
     * @return 对齐后的字符串
     */
    public static String ljust(String original, int length, char c) {
        return just(original, length, c, false);
    }

    /**
     * 字符串右对齐
     *
     * @param original 原始字符串
     * @param length   字符串总长度
     * @return 对齐后的字符串
     */
    public static String rjust(String original, int length) {
        return rjust(original, length, ' ');
    }

    /**
     * 字符串右对齐
     *
     * @param original 原始字符串
     * @param length   字符串总长度
     * @param c        补齐字符
     * @return 对齐后的字符串
     */
    public static String rjust(String original, int length, char c) {
        return just(original, length, c, true);
    }

    /**
     * 字符串对齐
     *
     * @param original 原始字符串
     * @param length   字符串总长度
     * @param c        补齐字符
     * @param right    是否是右对齐
     * @return 对齐后的字符串
     */
    private static String just(String original, int length, char c, boolean right) {
        Assert.isTrue(length > -1, "length must be greater than -1");
        if (length == 0 || (!StringUtils.isEmpty(original) && length <= original.length())) {
            return original;
        }
        StringBuilder builder = new StringBuilder();

        // 左对齐
        if (!right && !StringUtils.isEmpty(original)) {
            builder.append(original);
        }
        for (int i = 0, size = length - ObjectUtils.ifNull(original, String::length, () -> 0); i < size; i++) {
            builder.append(c);
        }

        // 右对齐
        if (right && !StringUtils.isEmpty(original)) {
            builder.append(original);
        }
        return builder.toString();
    }
}
